%code requires {
  #include <memory>
  #include <string>
  #include <utility>
  #include <variant>
  #include "../inc/ast.hpp"
  #include <cassert>
}

%{

#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <variant>
#include <cassert>

#include "../inc/ast.hpp"

// 声明 lexer 函数和错误处理函数
int yylex();
void yyerror(std::unique_ptr<BaseAST> &ast, const char *s);

using namespace std;

%}

// 定义 parser 函数和错误处理函数的附加参数
// lv1.3中, 参数类型是新定义的抽象类BaseAST的智能指针的引用
%parse-param { std::unique_ptr<BaseAST> &ast }

%union {
  std::string *str_val;
  int int_val;
  BaseAST* ast_val;
  OperatorType optype;
}

// lexer 返回的所有 token 种类的声明
// 注意 IDENT 和 INT_CONST 会返回 token 的值, 分别对应 str_val 和 int_val
%token INT RETURN
%token <str_val> IDENT OPERATOR
%token <int_val> INT_CONST

// 非终结符的类型定义
%type <ast_val> FuncDef Block Stmt Exp PrimaryExp UnaryExp Value
%type <str_val> FuncType
%type <optype> UnaryOp
// %type <int_val> Number

%%

CompUnit
  : FuncDef {
    // ast = unique_ptr<string>($1);
    auto comp_unit = make_unique<CompUnitAST>();
    comp_unit->func_defs.push_back(unique_ptr<BaseAST>($1));
    ast = std::move(comp_unit);
  }
  ;

FuncDef
  : FuncType IDENT '(' ')' Block {
    // auto type = unique_ptr<string>($1);
    // auto ident = unique_ptr<string>($2);
    // auto block = unique_ptr<string>($5);
    // $$ = new string(*type + " " + *ident + "() " + *block);

    auto ast = new FuncDefAST();
    ast->func_type = unique_ptr<string>($1);
    ast->ident = unique_ptr<string>($2);
    ast->blocks.push_back(unique_ptr<BaseAST>($5));
    $$ = ast;
  }
  ;

FuncType
  : INT {
    $$ = new string("int");
  }
  ;

Block
  : '{' Stmt '}' {
    auto ast = new BlockAST();
    ast->statements.push_back(unique_ptr<BaseAST>($2));
    $$ = ast;

  }
  ;

Stmt
  : RETURN Exp ';' {

    auto ast = new StmtAST();
    ast->exps.push_back(unique_ptr<BaseAST>($2));
    $$ = ast;

  }
  ;

Exp
  : UnaryExp {
    std::cout << "constructing Exp" << std::endl;
    auto ast = new ExpAST();
    ast->idx = $1->idx;
    ast->from = $1->from;
    ast->unary_exp = unique_ptr<BaseAST>($1);
    $$ = ast;
  }
  ;

UnaryExp
  : PrimaryExp {
    std::cout << "constructing UnaryExp in branch PrimaryExp" << std::endl;
    auto ast = new UnaryExpAST();
    ast->idx = $1->idx;
    ast->from = $1->from;
    ast->stores = UnaryExpAST::UnaryExpStorage::pexp;
    ast->pexp = unique_ptr<BaseAST>($1);
    $$ = ast;

  }
  | UnaryOp UnaryExp {
    std::cout << "constructing UnaryExp in branch op self" << std::endl;
    OperatorType op = $1;
    auto self = unique_ptr<BaseAST>($2);
    auto ast = new UnaryExpAST();
    ast->stores = UnaryExpAST::UnaryExpStorage::op_uexp;
    ast->idx = EXPRESSION_LIST.insert(make_tuple<OperatorType,
                               std::optional<std::tuple<ValueFrom, size_t>>,
                               std::optional<std::tuple<ValueFrom, size_t>>,
                               std::optional<std::tuple<ValueFrom, size_t>>>(move(op),make_tuple(self->from, self->idx) ,nullopt, nullopt));
    ast->from = ValueFrom::expression;
    // unique_ptr基本相当于Box, 单所有权智能指针, 无法以复制的方式传参
    // 解决方法就是把所有权转移走, 也就是移动语义
    // 感觉不如Rust
    ast->op_self = pair<OperatorType, unique_ptr<BaseAST>>(std::move(op), std::move(self));
    $$ = ast; 
  }
  ;

PrimaryExp
  : '(' Exp ')' {
    std::cout << "constructing PrimaryExp in branch (Exp)" << std::endl;
    auto ast = new PrimaryExpAST();
    ast->idx = $2->idx;
    ast->from = $2->from;
    ast->exp = unique_ptr<BaseAST>($2);
    ast->stores = PrimaryExpAST::PrimaryExpStorage::exp;

    $$ = ast;
  }
  | Value {
    std::cout << "constructing PrimaryExp in branch Value" << std::endl;
    auto ast = new PrimaryExpAST();
    // ast->idx = EXPRESSION_LIST.insert({nullopt, nullopt, nullopt, $1->idx});
    ast->idx = $1->idx;
    ast->from = $1->from;
    ast->val = unique_ptr<BaseAST>($1);
    ast->stores = PrimaryExpAST::PrimaryExpStorage::val;
    $$ = ast;
  }
  ;

Value
  : INT_CONST {

    cout << "constructing Value with " << $1 << endl;

    auto ast = new ValueAST();
    ast->type = ValueType::i32;
    ast->idx = SYMBOL_LIST.insert({ValueType::i32, $1});
    ast->from = ValueFrom::symbol;

    $$ = ast;

  }
  ;

UnaryOp 
  : OPERATOR {
    $$ = STR2OP.at(*$1);
  }
  ;

%%

// 定义错误处理函数, 其中第二个参数是错误信息
// parser 如果发生错误 (例如输入的程序出现了语法错误), 就会调用这个函数
// lv1.3中, 修改了参数类型
void yyerror(unique_ptr<BaseAST> &ast, const char *s) {
  cerr << "error: " << s << endl;
}
